﻿using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace DiscreteMath
{
	/// <summary>
	/// Defines a finite state machine over the specified alphabet and with the given states.
	/// </summary>
	/// <typeparam name="State">The type of the states used by the machine.</typeparam>
	/// <typeparam name="char">The alphabet to read words from.</typeparam>
    public abstract class FiniteStateMachine : ILanguageRecognizer
	{		
		protected FiniteStateMachine(
			ISet<State> states,
			ISet<char> alphabet,  
			State startState, 
			ISet<State> finalStates)
		{
			if (states == null)
				throw new ArgumentNullException("states");

			if (states.Count == 0)
				throw new ArgumentException("The states may not be an empty set!");

			if (startState == State.Undefined)
				throw new ArgumentException("startState state may not be undefined", "startState");

			if (finalStates == null)
				throw new ArgumentNullException("finalStates");

			if (!finalStates.IsSubsetOf(states))
				throw new ArgumentException("The set of finalStates states must be a subset of the set of all states!", "finalStates");

			if (alphabet == null)
				throw new ArgumentNullException("alphabet");

			if (alphabet.Count == 0)
				throw new ArgumentException("The alphabet may not be empty!");

			this.States = new HashSet<State>(states);
			this.Alphabet = alphabet;
			this.StartState = startState;
			this.Final = new HashSet<State>(finalStates);
		}

        [JsonProperty("states")]
		/// <summary>
		/// Gets the set of states that the automaton operates over.
		/// </summary>
		public ISet<State> States { get; private set; }

        [JsonProperty("alphabet")]
		/// <summary>
		/// Gets the set of alphabet that the automaton operates over.
		/// </summary>
		public ISet<char> Alphabet { get; private set; }

        [JsonProperty("startState")]
		/// <summary>
		/// Gets the starting state of the automaton.
		/// </summary>
		public State StartState { get; private set; }

        [JsonProperty("finalStates")]
		/// <summary>
		/// Gets the set of all final states for the automaton.
		/// </summary>
		public HashSet<State> Final { get; private set; }

		[JsonIgnore]
		/// <summary>
		/// Gets a value indicating whether the start state of the automaton is final.
		/// </summary>
		public bool IsStartStateFinal
		{
			get
			{
				return this.Final.Contains(this.StartState);
			}
		}

        /// <summary>
        /// Reads the given word and returns a value indicating whether the machine accepts it.
        /// </summary>
        /// <param name="word">The word to read.</param>
        /// <returns>True if the machine accepts the word, False otherwise.</returns>
		public abstract bool ReadWord(IEnumerable<char> word);
	}
}
